package network.udptools;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InterfaceAddress;
import java.net.NetworkInterface;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Enumeration;

import exceptions.MessageLengthException;

import network.TCP.ClientTCP;
import network.udptools.Interface.UDPSenderInterface;

/**
 * 
 * 
 * @author SIAGO HAPPI Christelle UDPSender : it is a dedicated outbound udp
 *         connections class
 * @param socketUdp
 *            : the listening socket
 * 
 */
public class UDPSender implements UDPSenderInterface {

	private DatagramSocket socketUdp;

	/**
	 * @param list
	 *            of the broadcast address of all the network interfaces
	 */
	private ArrayList<InetAddress> broadcastAddrList;

	/**
	 * @param list
	 *            of the broadcast address of all the network interfaces
	 */
	private ArrayList<InetAddress> ipAddrList;

	private boolean wifi;
	private Socket socket;

	private ClientTCP clientTCP;

	/*
	 * contructor
	 */
	public UDPSender() {

		wifi = false;
		broadcastAddrList = new ArrayList<InetAddress>();
		ipAddrList = new ArrayList<InetAddress>();

		// getBroadcastAdresses();
		// System.out.println("list ip address = " + ipAddrList);
		// System.out.println("list broadcast address = " + broadcastAddrList);

		try {

			socketUdp = new DatagramSocket(UDPSettings.UDP_PORT_ENVOI,
					InetAddress.getLocalHost());

		} catch (SocketException e) {
			e.printStackTrace();
			System.out
					.println("Error when creating the socket dedicated to sending udp");
		} catch (UnknownHostException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	/**
	 * method for sending messages unicast
	 */

	public void sendUnicastUDPMessage(byte[] messageByte, InetAddress adresse)
			throws MessageLengthException {

		try {
			if (messageByte.length > UDPSettings.UDP_BUFFER_SIZE)
				throw new MessageLengthException("Message too long");

			DatagramPacket packet = new DatagramPacket(messageByte,
					messageByte.length, adresse, UDPSettings.UDP_PORT_ECOUTE);
			socketUdp.send(packet);

		} catch (IOException e1) {
			e1.printStackTrace();
		}
	}

	/**
	 * method to send a signal (Bye, Hello) network broadcast
	 */
	public void sendBroadcastMessage(byte[] messageByte)
			throws MessageLengthException {
		try {
			if (messageByte.length > UDPSettings.UDP_BUFFER_SIZE)
				throw new MessageLengthException("Message too long");

			/*
			 * if (wifi) { for (InetAddress addr : broadcastAddrList) {
			 * System.out.println("send broadcast at " + addr); DatagramPacket
			 * packet = new DatagramPacket(messageByte, messageByte.length,
			 * addr, UDPSettings.UDP_PORT_ECOUTE); socketUdp.send(packet);
			 * System.out.println(" broadcast sent successfully to " + addr); }
			 * } else {
			 */
			DatagramPacket packet = new DatagramPacket(messageByte,
					messageByte.length,
					InetAddress.getByName("255.255.255.255"),
					UDPSettings.UDP_PORT_ECOUTE);
			socketUdp.send(packet);
			// }

		} catch (IOException e1) {

			e1.printStackTrace();
		}
	}

	/**
	 * close the socket
	 */
	public void stopSending() {
		socketUdp.close();
	}

	/**
	 * we want to retrieve the local IP address to which the socket is connected
	 * To not treat our own signals
	 */
	public InetAddress myIPAdress() {
		return this.socketUdp.getLocalAddress();
	}

	/**
	 * get broadcast addresses
	 * 
	 * @return
	 */
	public ArrayList<InetAddress> getBroadcastAddr() {
		return broadcastAddrList;
	}

	/**
	 * find the broadcast address of all the network interfaces
	 * 
	 * @return the broadcast addresses list
	 */
	private void getBroadcastAdresses() {
		// broadcast address list
		// all the network interfaces of the machine
		Enumeration<NetworkInterface> interfaces;
		try {
			interfaces = NetworkInterface.getNetworkInterfaces();
			while (interfaces.hasMoreElements()) {
				NetworkInterface networkInterface = interfaces.nextElement();

				if (!networkInterface.isLoopback())
					for (InterfaceAddress interfaceAddress : networkInterface
							.getInterfaceAddresses()) {
						InetAddress broadcast = interfaceAddress.getBroadcast();
						if (broadcast != null) {
							broadcastAddrList.add(broadcast);
							ipAddrList.add(interfaceAddress.getAddress());
						}
					}
			}
		} catch (SocketException e) {
			e.printStackTrace();
		}

	}

	/**
	 * 
	 * @return ipAddrList complete list of IP adress of local user
	 */
	public ArrayList<InetAddress> getIpAddrList() {
		return ipAddrList;
	}
}
